package com.example.vehiclefabric.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.vehiclefabric.DAO.VehicleDAOImpl;
import com.example.vehiclefabric.DTO.VehicleBean;
import com.example.vehiclefabric.DTO.VehicleDTO;
import com.example.vehiclefabric.VehicleFabricApplication;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.StringUtils;
import org.hyperledger.fabric.client.Contract;
import org.hyperledger.fabric.client.GatewayException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

import static cn.hutool.extra.spring.SpringUtil.getBean;

/**
 * @name: VehccController
 * @description: 定义车辆智能合约的交互方法。
 * @author: Lulu Zhao
 **/
@RestController
@RequestMapping("/vehcc")
@Slf4j
@AllArgsConstructor
public class VehccController {
    final Contract vehccContract = getBean("vehccContract", Contract.class);

    @Autowired
    private VehicleDAOImpl vehicleDAOImpl;

    /**
     * @name: queryAllVehicles
     * @description: 查询全部车辆信息。
     * @params: []
     * @return: java.util.Map<java.lang.String, java.lang.Object>
     * @author: Lulu Zhao
     */
    @GetMapping("/vehicles")
    public Map<String, Object> queryAllVehicles() throws GatewayException {

        Map<String, Object> result = Maps.newConcurrentMap();

        byte[] account = vehccContract.evaluateTransaction("QueryAllVehicles");

        result.put("payload", StringUtils.newStringUtf8(account));

        result.put("status", "ok");

        return result;
    }

    /**
     * @name: registerVehicle
     * @description: 注册新车辆。
     * @params: [vehicleDTO]
     * @return: java.util.Map<java.lang.String, java.lang.Object>
     * @author: Lulu Zhao
     */
    @PostMapping("/register")
    public Map<String, Object> registerVehicle(VehicleDTO vehicleDTO) throws GatewayException {

        Map<String, Object> result = Maps.newConcurrentMap();

        vehccContract.newProposal("RegisterVehicle")
                .addArguments(vehicleDTO.getVehicleId(), vehicleDTO.getHashedPassword(), vehicleDTO.getCurrentHashTime(), vehicleDTO.getCurrentState(), vehicleDTO.getLocation())
                .build()
                .endorse()
                .submitAsync();

        result.put("status", "ok");

        return result;
    }

    /**
     * @name: updateVehicle
     * @description: 更新车辆信息。
     * @params: [vehicleDTO]
     * @return: java.util.Map<java.lang.String, java.lang.Object>
     * @author: Lulu Zhao
     */
    @PostMapping("/update")
    public Map<String, Object> updateVehicle(VehicleDTO vehicleDTO) throws GatewayException {

        Map<String, Object> result = Maps.newConcurrentMap();

        vehccContract.newProposal("UpdateVehicleState")
                .addArguments(vehicleDTO.getVehicleId(), vehicleDTO.getHashedPassword(), vehicleDTO.getCurrentHashTime(), vehicleDTO.getCurrentState(), vehicleDTO.getLocation())
                .build()
                .endorse()
                .submitAsync();

        result.put("status", "ok");

        return result;
    }

    /**
     * @name: queryVehicleById
     * @description: 根据车辆ID查询车辆信息，用于判断车辆是否存在。
     * @params: [vehicleId]
     * @return: java.util.Map<java.lang.String, java.lang.Object>
     * @author: Lulu Zhao
     */
    @GetMapping("/queryVehicleById")
    public Map<String, Object> queryVehicleById(String vehicleId) {

        Map<String, Object> result = Maps.newConcurrentMap();

        try {
            vehccContract.evaluateTransaction("QueryVehicle", vehicleId);
        } catch (GatewayException gatewayException) {
            result.put("status", "failed");
            return result;
        }

        result.put("status", "ok");

        return result;
    }

    /**
     * @name: validateVehicle
     * @description: 验证车辆身份并发放公钥证书。
     * @params: [vehicleId, previousHashedPassword]
     * @return: java.util.Map<java.lang.String, java.lang.Object>
     * @author: Lulu Zhao
     */
    @GetMapping("/validate")
    public Map<String, Object> validateVehicle(String vehicleId, String previousHashedPassword) throws GatewayException {

        Map<String, Object> result = Maps.newConcurrentMap();

        byte[] account_byte = vehccContract.evaluateTransaction("ValidateVehicle", vehicleId);

        String account_str = StringUtils.newStringUtf8(account_byte);

        JSONObject responseJson = JSONUtil.parseObj(account_str);
        String currentHashTime = responseJson.get("CurrentHashTime").toString();
        String hashedPassword = responseJson.get("HashedPassword").toString();
        VehicleFabricApplication.firstLoginFlag = currentHashTime.equals("30");

        if (SmUtil.sm3(previousHashedPassword).equals(hashedPassword)) {
            if (Integer.parseInt(currentHashTime) < 3) {
                result.put("status", "needToBeUpdated");
                result.put("payload", String.valueOf(Integer.parseInt(currentHashTime) - 1));
                result.put("certificate", VehicleFabricApplication.publicKey);
                return result;
            }
            vehccContract.newProposal("UpdateVehicleState")
                    .addArguments(vehicleId, previousHashedPassword, String.valueOf(Integer.parseInt(currentHashTime) - 1), "inuse", "0,0")
                    .build()
                    .endorse()
                    .submitAsync();
            result.put("status", "ok");
            result.put("payload", String.valueOf(Integer.parseInt(currentHashTime) - 1));
            result.put("certificate", VehicleFabricApplication.publicKey);
        } else {
            result.put("status", "failed");
        }
        return result;
    }

    /**
     * @name: findAll
     * @description: 测试RSU数据库连接，查询全部已完成密钥协商的车辆信息。
     * @params: []
     * @return: void
     * @author: Lulu Zhao
     */
    @GetMapping("/findall")
    public void findAll() {
        List<VehicleBean> list = vehicleDAOImpl.findAll();
        for (VehicleBean vehicleBean : list) {
            System.out.println(vehicleBean.getVehicleId() + "  " + vehicleBean.getKeySM4() + "  " + vehicleBean.getKeyHmac());
        }
    }

    /**
     * @name: keyAgreement
     * @description: 密钥协商过程：获取对称密钥，存入数据库并返回状态。
     * @params: [vehicleId, message]
     * @return: java.util.Map<java.lang.String, java.lang.Object>
     * @author: Lulu Zhao
     */
    @PostMapping("/keyagreement")
    public Map<String, Object> keyAgreement(String vehicleId, String message) {
        Map<String, Object> result = Maps.newConcurrentMap();
        String[] messages = message.split("@");
        SM2 sm2 = new SM2(VehicleFabricApplication.privateKey, null);
        byte[] decryptMessage = sm2.decrypt(Base64.decode(messages[0]), KeyType.PrivateKey);
        byte[] KeySM4 = new byte[16];
        byte[] KeyHmac = new byte[7];
        System.arraycopy(decryptMessage, 0, KeySM4, 0, 16);
        System.arraycopy(decryptMessage, 16, KeyHmac, 0, decryptMessage.length - 16);
        VehicleBean vehicleBean = new VehicleBean(vehicleId, Base64.encode(KeySM4), Base64.encode(KeyHmac));
        HMac mac = new HMac(HmacAlgorithm.HmacSM3, KeyHmac);
        if (mac.digestHex(messages[0]).equals(messages[1])) {
            if (VehicleFabricApplication.firstLoginFlag) {
                if (vehicleDAOImpl.addVehicle(vehicleBean)) {
                    result.put("status", "ok");
                }
            } else {
                if (vehicleDAOImpl.updateByVehicleId(vehicleBean.getVehicleId(), vehicleBean.getKeySM4(), vehicleBean.getKeyHmac())) {
                    result.put("status", "ok");
                }
            }
        } else {
            result.put("status", "failed");
        }
        return result;
    }
}
